use std::{fs, io, path::Path};

use colored::Colorize;
use rig::{completion::ToolDefinition, tool::Tool};
use serde::{Deserialize, Serialize};
use serde_json::json;
use treeline::Tree;

#[derive(Deserialize)]
pub struct OperationArgs {
    path: String,
}

#[derive(Debug, thiserror::Error)]
#[error("Math error")]
pub struct TreeError;

#[derive(Deserialize, Serialize)]
pub struct TreeCmd {
    pub project_path: String,
}
impl Tool for TreeCmd {
    const NAME: &'static str = "add";

    type Error = TreeError;
    type Args = OperationArgs;
    type Output = String;

    async fn definition(&self, _prompt: String) -> ToolDefinition {
        ToolDefinition {
            name: "add".to_string(),
            description: "输出目录树,最多显示3层,当前项目使用\".\"".to_string(),
            parameters: json!({
                "type": "object",
                "properties": {
                    "path": {
                        "type": "string",
                        "description": "the path needed to known"
                    }
                }
            }),
        }
    }

    async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
        println!("{}", format!("tree {}", args.path).red());
        let res = tree_cmd(&self.project_path, args.path);
        Ok(res)
    }
}

fn label<P: AsRef<Path>>(p: P) -> String {
    p.as_ref().file_name().unwrap().to_str().unwrap().to_owned()
}

fn tree<P: AsRef<Path>>(p: P, max_deep: u32) -> io::Result<Tree<String>> {
    if max_deep == 0 {
        return Ok(Tree::root(label(p.as_ref().canonicalize()?)));
    }

    let result = fs::read_dir(&p)?.into_iter().filter_map(|e| e.ok()).fold(
        Tree::root(label(p.as_ref().canonicalize()?)),
        |mut root, entry| {
            let dir = entry.metadata().unwrap();
            if !entry.file_name().to_str().expect("").starts_with(".") {
                if dir.is_dir() {
                    root.push(tree(entry.path(), max_deep - 1).unwrap());
                } else {
                    root.push(Tree::root(label(entry.path())));
                }
            }
            root
        },
    );
    Ok(result)
}

// 输出树状图
fn tree_cmd(pwd: &String, path: String) -> String {
    let base_path = Path::new(pwd);
    let real_path_result = base_path
        .join(&path)
        .canonicalize()
        .map(|it| it.display().to_string());

    if let Ok(real_path) = real_path_result {
        if let Ok(tree_result) = tree(&real_path, 3) {
            return format!("目录树 ’{}‘\n{}", path, tree_result);
        } else {
            return format!("路径 ’{}‘ 不存在", path);
        }
    } else {
        return format!("路径 ’{}‘ 不存在", path);
    }
}

#[test]
fn pathTest() {
    let real_path_result = Path::new(&"/home/x/code/RuoYi".to_string())
        .join(".".to_string())
        .canonicalize()
        .map(|it| it.display().to_string());

    if let Ok(real_path) = real_path_result {
        if let Ok(tree_result) = tree(&real_path, 4) {
            println!("{}", tree_result);
        }
    }
}
